package com.wh1200.mybatis.xmlless.model

import com.wh1200.kotlin.isNotNullAndNotBlank
import com.wh1200.mybatis.xmlless.AnnotatedElementDetailInfo
import com.wh1200.mybatis.xmlless.model.base.IExpressionToken
import com.wh1200.mybatis.xmlless.model.criteria.CriteriaParameter
import com.wh1200.mybatis.xmlless.model.expression.Operator
import com.wh1200.mybatis.xmlless.model.expression.PropertyRef
import com.wh1200.mybatis.xmlless.model.expression.Token
import com.wh1200.mybatis.xmlless.resolver.ParameterResolver

/**
 *
 * @author 吴昊
 * @date 2023/12/8 11:09
 * @since v4.0.0
 * @version 1.0
 */
class TestInfo(
    private val expression: String,
    private val field: AnnotatedElementDetailInfo
) {

  companion object {
    private val testOperators = listOf(
        ">", "&lt;", "&gt;", "&lt;=", "&gt;=", "<", ">=", "<=", "!=", "=", "=="
    )
    private val connectOperators = listOf(
        "and", "or", "&&", "||"
    )
  }

  fun getExpression(parameters: List<CriteriaParameter>, methodInfo: MethodInfo): String {
    val name = parameters.find { it.element == field }?.name
    return preHandleIfExpression(expression, field, name?.toString(), methodInfo)
  }

  private fun preHandleIfExpression(
      expression: String,
      field: AnnotatedElementDetailInfo,
      specificName: String?,
      methodInfo: MethodInfo
  ): String {
    val name = when {
      specificName.isNotNullAndNotBlank() -> specificName
      else                                -> {
        ParameterResolver.resolve(field.paramName, methodInfo)!!.name
      }
    }
    val list = mutableListOf<IExpressionToken>()
    val parts = expression.split("\\s+".toRegex())
    parts.forEachIndexed { index, it ->
      val start = parts.take(index).sumOf { it.length } + index
      val end = start + it.length
      when {
        it.lowercase() in connectOperators               -> {
          list.add(ConnectOperator(it.lowercase(), start, end))
        }

        it in testOperators                              -> {
          if ((list.isEmpty() || list.last() is ConnectOperator
                  || list.last().toString().endsWith(")"))
          ) {
            list.add(PropertyRef(name, start, end))
          }
          list.add(Operator(it, start, end))
        }

        list.isEmpty() || list.last() is ConnectOperator -> {
          val matchedParameter = ParameterResolver.resolve(it, methodInfo)
          if (matchedParameter != null) {
            list.add(PropertyRef(matchedParameter.name, start, end))
          } else {
            list.add(PropertyRef(it, start, end))
          }
        }

        else                                             -> {
          list.add(Token(it, start, end))
        }
      }
    }
    return list.joinToString(" ").replace(">", "&gt;")
        .replace("<", "&lt;")
  }

}

class ConnectOperator(private val op: String, start: Int, end: Int) : IExpressionToken(start, end) {

  override fun toSql(): String {
    return op
  }

}
